package net.sourceforge.openclixml;

import net.sourceforge.openclixml.autogen.FormulaType;
import net.sourceforge.openclixml.environment.IEnvironment;
import net.sourceforge.openclixml.util.ExceptionUtil;

import org.w3c.dom.Document;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
 * This abstract class is the root-class for all CLiXML elements. JAXB Generated and Extending classes. This element implements the IEvaluator interface. Inherit from this class to implement new language elements.
 *
 * This offers similar like JDOM a getter method to get a/the children of this element of type formulatype. JAXB creates for each element a getter method, which is quite annoying. If one wants to get a child element it's quite annoying to call them yourself. This class helps by invoking these getter methodsThis class gets Children any ClixmlElement.
 *
 * @version $Revision: 49 $
 * @author dominikjungo
 *
 * Last changed: $Date: 2008-04-09 22:06:24 +0200 (Wed, 09 Apr 2008) $
 * The License for this file can be found in doc/Licenses/license-OpenCliXML.txt
 */
public abstract class ClixmlElement implements IElement, IEvaluator {
    /**
     * {@inheritDoc}
     */
    public final boolean eval(final IEnvironment<String, String> environment,
        final Map<String, Document> systemUnderTest)
        throws EvaluationFailedException {
        evalDelegated(environment, systemUnderTest);
        assert isSuccess() != null : "eval did not bring any result. class: " +
        getClass().getName();

        return isSuccess();
    }

    /**
     * {@inheritDoc}
     */
    public boolean evalDelegated(
        final IEnvironment<String, String> environment,
        final Map<String, Document> systemUnderTest)
        throws EvaluationFailedException {
        throw new UnsupportedOperationException("Not implemented yet.");
    }

    /**
     * {@inheritDoc}
     */
    public Boolean isSuccess() {
        throw new UnsupportedOperationException("Not implemented yet.");
    }

    /**
     * {@inheritDoc}
     * @return a deep clone of this element
     * @throws java.lang.CloneNotSupportedException
     */
    @Override
    public Object clone() throws CloneNotSupportedException {
        try {
            ObjectOutputStream oos = null;

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            oos.writeObject(this);

            byte[] buf = baos.toByteArray();
            oos.close();

            // deserialize byte array into ArrayList
            ByteArrayInputStream bais = new ByteArrayInputStream(buf);
            ObjectInputStream ois = new ObjectInputStream(bais);
            Object o = ois.readObject();
            ois.close();

            return o;
        } catch (Exception ex) {
            Logger.getLogger(ClixmlElement.class.getName())
                  .log(Level.SEVERE, null, ex);
            throw new CloneNotSupportedException("");
        }
    }

    /**
     * Gets the child element. This is done by invoking all getter methods with returntype FormulaType.
     * @return The child of the element passed as parameter.
     * @throws net.sourceforge.openclixml.ClixmlException if no child is found.
     */
    public net.sourceforge.openclixml.ext.FormulaType getChildElement()
        throws ClixmlException {
        net.sourceforge.openclixml.ext.FormulaType child = null;
        Logger logger = Logger.getLogger(FormulaType.class.getName());

        for (Method method : getClass().getMethods()) {
            String name = method.getName();
            Class[] params = method.getParameterTypes();
            Class returntype = method.getReturnType();

            if (name.startsWith("get") && (params.length == 0) &&
                    !name.equals("getChildElement")) {
                boolean isFormula = false;

                try {
                    if (returntype.newInstance() instanceof FormulaType) {
                        isFormula = true;
                    }
                } catch (IllegalAccessException e) {
                    logger.finest(ExceptionUtil.EATENEXCEPTION +
                        ExceptionUtil.trowableToString(e));

                    //seems to be ok. Not treated as a formula-return-type-method 
                } catch (InstantiationException e) {
                    //seems to be ok. Not treated as a formula-return-type-method
                    logger.finest(ExceptionUtil.EATENEXCEPTION +
                        ExceptionUtil.trowableToString(e));
                }

                if (isFormula) {
                    try {
                        child = (net.sourceforge.openclixml.ext.FormulaType) method.invoke(this,
                                (Object[]) null);

                        if (child != null) {
                            //finally a child is found which is not null
                            return child;
                        }
                    } catch (IllegalAccessException ex) {
                        //seems to be ok.
                        logger.finest(ExceptionUtil.EATENEXCEPTION +
                            ExceptionUtil.trowableToString(ex));
                    } catch (IllegalArgumentException ex) {
                        //seems to be ok. Wrong arguments. This is not the getter i was looking for
                        logger.finest(ExceptionUtil.EATENEXCEPTION +
                            ExceptionUtil.trowableToString(ex));
                    } catch (InvocationTargetException ex) {
                        //seems to be ok.
                        logger.finest(ExceptionUtil.EATENEXCEPTION +
                            ExceptionUtil.trowableToString(ex));
                    }
                }
            }
        }

        throw new ClixmlException("no child found");
    }

    /**
     * {@inheritDoc}
     */
    public void setChild(final net.sourceforge.openclixml.autogen.FormulaType child) {
        Logger logger = Logger.getLogger(FormulaType.class.getName());

        try {
            String setterName = "set" +
                child.getClass().getSimpleName().split("Type")[0];

            Method method = this.getClass()
                                .getMethod(setterName,
                    child.getClass().getSuperclass());
            method.invoke(this, child);
        } catch (Exception ex) {
            logger.finest(ExceptionUtil.EATENEXCEPTION +
                ExceptionUtil.trowableToString(ex));
        }
    }

    /**
     * {@inheritDoc}
     */
    public void setChildNull() {
        Logger logger = Logger.getLogger(FormulaType.class.getName());

        try {
            FormulaType child = getChildElement();
            String setterName = "set" +
                child.getClass().getSimpleName().split("Type")[0];

            Method method = getClass()
                                .getMethod(setterName,
                    child.getClass().getSuperclass());
            method.invoke(this, (Object) null);
        } catch (Exception ex) {
            logger.finest(ExceptionUtil.EATENEXCEPTION +
                ExceptionUtil.trowableToString(ex));
        }
    }
}
